using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;

namespace XsltHelper
{
	public class XmlRichTextBox : RichTextBox
	{
		protected override void OnHandleCreated(EventArgs e) {
			base.OnHandleCreated(e);
			int[] stops = { 8 };
			Win32.SendMessage(Handle, Win32.EM_SETTABSTOPS, 1, stops);
		}

		protected override void OnKeyUp(KeyEventArgs e) {
			base.OnKeyDown(e);
			if (e.KeyCode == Keys.Enter)
				Reformat(e);
		}

		protected override void OnKeyPress(KeyPressEventArgs e) {
			base.OnKeyPress(e);
			switch (e.KeyChar) {
				case '>':
					HandleEndBracket(e);
					break;
				case '=':
					HandleAttribute(e);
					break;
			}
		}


		private void Reformat(KeyEventArgs e) {
			int tabCount = 0;
			CharPoint point = this.CaretLCPosition;

			string previousLine = string.Empty;
			if (point.Line > 0)
				previousLine = this.Lines[point.Line-1];
			tabCount = GetTabCount(previousLine, 0);
			string tabs = string.Empty;
			for (int i = 0; i < tabCount; i++) {
				tabs += "\t";
			}
			if (this.Lines[point.Line].Trim() != string.Empty)
				tabs = tabs + "\n" + tabs;
			this.Text = this.Text.Insert(point.AbsolutePosition, tabs);
			if(tabCount == 0)
				this.SelectionStart = point.AbsolutePosition + tabCount;
			else
				this.SelectionStart = point.AbsolutePosition + tabCount;
		}

		private int GetTabCount(string line, int startIndex) {
			if (line.Length <= startIndex)
				return 0;

			if (line.Substring(startIndex, 1) == "\t")
				return 1 + GetTabCount(line, startIndex + 1);
			return 0;
		}



		private void HandleAttribute(KeyPressEventArgs e) {
			CharPoint point = this.CaretLCPosition;
			string start = this.Text.Substring(0, point.AbsolutePosition);
			string end = string.Empty;
			if (point.AbsolutePosition < this.Text.Length)
				end = this.Text.Substring(point.AbsolutePosition);
			string padding = string.Empty;
			if (!end.StartsWith(" "))
				padding = " ";
			this.Text = start + "=\"\"" + padding + end;
			e.Handled = true;
			this.SelectionStart = point.AbsolutePosition + 2;
		}

		private void HandleEndBracket(KeyPressEventArgs e) {
			CharPoint point = this.CaretLCPosition;
			if (point.AbsolutePosition <= 1)
				return;
			if (this.Text[point.AbsolutePosition - 1] == '/')
				return;
			string search = this.Text.Substring(0, point.AbsolutePosition);
			string end = string.Empty;
			if (point.AbsolutePosition < this.Text.Length)
				end = this.Text.Substring(point.AbsolutePosition);
			int openingBracket = search.LastIndexOf('<');
			if (openingBracket < 0)
				return;
			int wordEnd = search.IndexOf(' ', openingBracket);
			if (wordEnd < 0)
				wordEnd = point.AbsolutePosition;
			string elementName = search.Substring(openingBracket + 1, wordEnd - openingBracket - 1);
			this.Text = search + "></" + elementName + ">" + end;
			e.Handled = true;
			this.SelectionStart = point.AbsolutePosition + 1;
		}

		/// <summary>
		/// Gets the caret current (L, C) position.
		/// </summary>
		/// <value>
		/// CharPoint struct
		/// </value>
		public CharPoint CaretLCPosition {
			get {
				CharPoint cpt = CharPoint.Empty;
				// save the handle reference for the ExtToolBox
				HandleRef hr = new HandleRef(this, base.Handle);
				// Send the EM_LINEFROMCHAR message with the value of
				// -1 in wParam.
				// The return value is the zero-based line number
				// of the line containing the caret.
				int l = (int)Win32.SendMessage(hr, Win32.EM_LINEFROMCHAR, -1, 0);
				// Send the EM_GETSEL message to the ToolBox control.
				// The low-order word of the return value is the
				// character position of the caret relative to the
				// first character in the ToolBox control,
				// i.e. the absolute character index.
				int sel = (int)Win32.SendMessage(hr, Win32.EM_GETSEL, 0, 0);
				// get the low-order word from sel
				int ai = sel & 0xffff;
				// Send the EM_LINEINDEX message with the value of -1
				// in wParam.
				// The return value is the number of characters that
				// precede the first character in the line containing
				// the caret.
				int li = (int)Win32.SendMessage(hr, Win32.EM_LINEINDEX, -1, 0);
				// Subtract the li (line index) from the ai
				// (absolute character index),
				// The result is the column number of the caret position
				// in the line containing the caret.
				int c = ai - li;
				// Add 1 to the l and c since these are zero-based.
				// Return a CharPoint with the caret current (L,C)
				// position
				return new CharPoint(l, c, ai);
			}
		}
	}
}